/**
 * @file ble_fcs.c
 * @brief 蓝牙烽火通信服务（fiberhome communication server）
 * @author wangh (wanghuan3037@fiberhome.com)
 * @version 1.0
 * @date 2021-02-02
 * @copyright Copyright (c) 2021  烽火通信
 */ 
#include "sdk_common.h"
#if NRF_MODULE_ENABLED(BLE_FCS)
#include "ble.h"
#include "ble_fcs.h"
#include "ble_srv_common.h"

#define NRF_LOG_MODULE_NAME ble_fcs
#if BLE_FCS_CONFIG_LOG_ENABLED
#define NRF_LOG_LEVEL       BLE_FCS_CONFIG_LOG_LEVEL
#define NRF_LOG_INFO_COLOR  BLE_FCS_CONFIG_INFO_COLOR
#define NRF_LOG_DEBUG_COLOR BLE_FCS_CONFIG_DEBUG_COLOR
#else // BLE_FCS_CONFIG_LOG_ENABLED
#define NRF_LOG_LEVEL       0
#endif // BLE_FCS_CONFIG_LOG_ENABLED
#include "nrf_log.h"
NRF_LOG_MODULE_REGISTER();


#define BLE_UUID_FCS_DT_CHARACTERISTIC  0x2A9F  ///< 基于 SIG-UUID of FCS数据传输DT特征


/**
 * @brief 蓝牙连接事件处理 @ref BLE_GAP_EVT_CONNECTED
 * @param[in] p_fcs     FCS 服务结构体
 * @param[in] p_ble_evt 蓝牙协议栈事件
 */
static void on_connect(ble_fcs_t * p_fcs, ble_evt_t const * p_ble_evt)
{
    ret_code_t                 err_code;
    ble_fcs_evt_t              evt;
    ble_gatts_value_t          gatts_val;
    uint8_t                    cccd_value[2];
    ble_fcs_client_context_t * p_client = NULL;

    err_code = blcm_link_ctx_get(p_fcs->p_link_ctx_storage,
                                 p_ble_evt->evt.gap_evt.conn_handle,
                                 (void *) &p_client);
    if (err_code != NRF_SUCCESS)
    {
        NRF_LOG_ERROR("Link context for 0x%02X connection handle could not be fetched.",
                      p_ble_evt->evt.gap_evt.conn_handle);
    }

    /* 检查hosts CCCD值，以通知已准备好使用DT特征发送数据 */
    memset(&gatts_val, 0, sizeof(ble_gatts_value_t));
    gatts_val.p_value = cccd_value;
    gatts_val.len     = sizeof(cccd_value);
    gatts_val.offset  = 0;

    err_code = sd_ble_gatts_value_get(p_ble_evt->evt.gap_evt.conn_handle,
                                      p_fcs->dt_handles.cccd_handle,
                                      &gatts_val);

    if ((err_code == NRF_SUCCESS)     &&
        (p_fcs->data_handler != NULL) &&
        ble_srv_is_notification_enabled(gatts_val.p_value))
    {
        if (p_client != NULL)
        {
            p_client->is_notification_enabled = true;
        }

        memset(&evt, 0, sizeof(ble_fcs_evt_t));
        evt.type        = BLE_FCS_EVT_COMM_STARTED;
        evt.p_fcs       = p_fcs;
        evt.conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
        evt.p_link_ctx  = p_client;

        p_fcs->data_handler(&evt);
    }
}


/**@brief Function for handling the @ref BLE_GATTS_EVT_WRITE event from the SoftDevice.
 *
 * @param[in] p_fcs     Nordic UART Service structure.
 * @param[in] p_ble_evt Pointer to the event received from BLE stack.
 */
static void on_write(ble_fcs_t * p_fcs, ble_evt_t const * p_ble_evt)
{
    ret_code_t                    err_code;
    ble_fcs_evt_t                 evt;
    ble_fcs_client_context_t    * p_client;
    ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;

    err_code = blcm_link_ctx_get(p_fcs->p_link_ctx_storage,
                                 p_ble_evt->evt.gatts_evt.conn_handle,
                                 (void *) &p_client);
    if (err_code != NRF_SUCCESS)
    {
        NRF_LOG_ERROR("Link context for 0x%02X connection handle could not be fetched.",
                      p_ble_evt->evt.gatts_evt.conn_handle);
    }

    memset(&evt, 0, sizeof(ble_fcs_evt_t));
    evt.p_fcs       = p_fcs;
    evt.conn_handle = p_ble_evt->evt.gatts_evt.conn_handle;
    evt.p_link_ctx  = p_client;

    if ((p_evt_write->handle == p_fcs->dt_handles.cccd_handle) &&
        (p_evt_write->len == 2))
    {
        if (p_client != NULL)
        {
            if (ble_srv_is_notification_enabled(p_evt_write->data))
            {
                p_client->is_notification_enabled = true;
                evt.type                          = BLE_FCS_EVT_COMM_STARTED;
            }
            else
            {
                p_client->is_notification_enabled = false;
                evt.type                          = BLE_FCS_EVT_COMM_STOPPED;
            }

            if (p_fcs->data_handler != NULL)
            {
                p_fcs->data_handler(&evt);
            }

        }
    }
    else if ((p_evt_write->handle == p_fcs->dt_handles.value_handle) &&
             (p_fcs->data_handler != NULL))
    {
        evt.type                  = BLE_FCS_EVT_RX_DATA;
        evt.params.rx_data.p_data = p_evt_write->data;
        evt.params.rx_data.length = p_evt_write->len;

        p_fcs->data_handler(&evt);
    }
    else
    {
        // Do Nothing. This event is not relevant for this service.
    }
}


/**@brief Function for handling the @ref BLE_GATTS_EVT_HVN_TX_COMPLETE event from the SoftDevice.
 *
 * @param[in] p_fcs     Nordic UART Service structure.
 * @param[in] p_ble_evt Pointer to the event received from BLE stack.
 */
static void on_hvx_tx_complete(ble_fcs_t * p_fcs, ble_evt_t const * p_ble_evt)
{
    ret_code_t                 err_code;
    ble_fcs_evt_t              evt;
    ble_fcs_client_context_t * p_client;

    err_code = blcm_link_ctx_get(p_fcs->p_link_ctx_storage,
                                 p_ble_evt->evt.gatts_evt.conn_handle,
                                 (void *) &p_client);
    if (err_code != NRF_SUCCESS)
    {
        NRF_LOG_ERROR("Link context for 0x%02X connection handle could not be fetched.",
                      p_ble_evt->evt.gatts_evt.conn_handle);
        return;
    }

    if (p_client->is_notification_enabled)
    {
        memset(&evt, 0, sizeof(ble_fcs_evt_t));
        evt.type        = BLE_FCS_EVT_TX_RDY;
        evt.p_fcs       = p_fcs;
        evt.conn_handle = p_ble_evt->evt.gatts_evt.conn_handle;
        evt.p_link_ctx  = p_client;

        p_fcs->data_handler(&evt);
    }
}

/**@brief FCS 服务蓝牙事件处理函数 */
void ble_fcs_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
    if ((p_context == NULL) || (p_ble_evt == NULL))
    {
        return;
    }

    ble_fcs_t * p_fcs = (ble_fcs_t *)p_context;

    switch (p_ble_evt->header.evt_id)
    {
        case BLE_GAP_EVT_CONNECTED:
            on_connect(p_fcs, p_ble_evt);
            break;

        case BLE_GATTS_EVT_WRITE:
            on_write(p_fcs, p_ble_evt);
            break;

        case BLE_GATTS_EVT_HVN_TX_COMPLETE:
            on_hvx_tx_complete(p_fcs, p_ble_evt);
            break;

        default:
            // No implementation needed.
            break;
    }
}


uint32_t ble_fcs_init(ble_fcs_t * p_fcs, ble_fcs_init_t const * p_fcs_init)
{
    ret_code_t            err_code;
    ble_uuid_t            ble_uuid;
    ble_add_char_params_t add_char_params;

    VERIFY_PARAM_NOT_NULL(p_fcs);
    VERIFY_PARAM_NOT_NULL(p_fcs_init);

    /* 初始化服务结构体 */
    p_fcs->data_handler = p_fcs_init->data_handler;

    /* 添加基于 SIG-UUID 的基础 UUID */
    BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_FCS_SERVICE);

    /* 添加 FCS 服务到协议栈 */
    err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,
                                        &ble_uuid,
                                        &p_fcs->service_handle);
    VERIFY_SUCCESS(err_code);


    // 添加 DT Characteristic 到协议栈
    memset(&add_char_params, 0, sizeof(add_char_params));
    add_char_params.uuid              = BLE_UUID_FCS_DT_CHARACTERISTIC;
    add_char_params.uuid_type         = p_fcs->uuid_type;
    add_char_params.max_len           = BLE_FCS_MAX_DATA_LEN;
    add_char_params.init_len          = sizeof(uint8_t);
    add_char_params.is_var_len        = true;
    add_char_params.char_props.notify = 1;
    add_char_params.char_props.read   = 1;
    add_char_params.char_props.write  = 1;
    add_char_params.char_props.write_wo_resp = 1;

    add_char_params.read_access       = SEC_OPEN;
    add_char_params.write_access      = SEC_OPEN;
    add_char_params.cccd_write_access = SEC_OPEN;

    return characteristic_add(p_fcs->service_handle, &add_char_params, &p_fcs->dt_handles);
    /**@snippet [Adding proprietary characteristic to the SoftDevice] */
}

/**@brief 蓝牙服务数据发送 */
uint32_t ble_fcs_data_send(ble_fcs_t * p_fcs,
                           uint8_t   * p_data,
                           uint16_t  * p_length,
                           uint16_t    conn_handle)
{
    ret_code_t                 err_code;
    ble_gatts_hvx_params_t     hvx_params;
    ble_fcs_client_context_t * p_client;

    VERIFY_PARAM_NOT_NULL(p_fcs);

    err_code = blcm_link_ctx_get(p_fcs->p_link_ctx_storage, conn_handle, (void *) &p_client);
    VERIFY_SUCCESS(err_code);

    if ((conn_handle == BLE_CONN_HANDLE_INVALID) || (p_client == NULL))
    {
        return NRF_ERROR_NOT_FOUND;
    }

    if (!p_client->is_notification_enabled)
    {
        return NRF_ERROR_INVALID_STATE;
    }

    if (*p_length > BLE_FCS_MAX_DATA_LEN)
    {
        return NRF_ERROR_INVALID_PARAM;
    }

    memset(&hvx_params, 0, sizeof(hvx_params));

    hvx_params.handle = p_fcs->dt_handles.value_handle;
    hvx_params.p_data = p_data;
    hvx_params.p_len  = p_length;
    hvx_params.type   = BLE_GATT_HVX_NOTIFICATION;

    return sd_ble_gatts_hvx(conn_handle, &hvx_params);
}


#endif // NRF_MODULE_ENABLED(BLE_FCS)

/*********** (C) COPYRIGHT 2021 FiberHome *****END OF FILE****/
